package day_04_04_ReadWriteLock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @Description
 * @Author shihan
 * @Date 2020/11/1 17:37
 * @Version 1.0
 * 1、多个线程同时读一个资源类没有任何问题，所以为了满足并发量，读取共享资源应该可以同时进行；
 * 2、但是，如果有一个线程想去写共享资源，那么就不应该再有其他线程，可以对共享资源进行读或写操作；
 *【小总结】
 *      读-读 可以共存
 *      读-写 不可以共存
 *      写-写 不可以共存
 */
public class ReadWriteLockDemo {

    public static void main(String[] args) {

        MyCache myCache = new MyCache();

        for (int i = 1; i <= 5 ; i++) {
            final int finalInt = i;
            new Thread(() -> {
                myCache.put(finalInt + "",finalInt);
            },i + "--put").start();
        }

        for (int i = 1; i <= 5 ; i++) {
            final int finalInt = i;
            new Thread(() -> {
                myCache.get(finalInt + "");
            },i + "--get").start();
        }

        for (int i = 6; i <= 10 ; i++) {
            final int finalInt = i;
            new Thread(() -> {
                myCache.put(finalInt + "",finalInt);
            },i + "--put").start();
        }

        for (int i = 6; i <= 10 ; i++) {
            final int finalInt = i;
            new Thread(() -> {
                myCache.get(finalInt + "");
            },i + "--get").start();
        }

    }
}

/**
 *【ReadWriteLock 读写锁初步总结】
 * 1、ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 * 2、readWriteLock.writeLock() 获取写锁
 * 3、readWriteLock.readLock() 获取读锁
 * 4、写锁，同一时间，只能有一个线程占有 写锁，进行写操作；
 * 5、读锁，同一时间，可以有多个线程同时占有 读锁，进行读操作；
 * 6.ReentrantReadWriteLock 读写锁的实现中，读锁使用共享模式,写锁使用独占模式；
 *   换句话说，读锁可以在没有写锁的时候被多个线程同时持有，写锁是独占的；
 * 7.ReentrantReadWriteLock 读写锁的实现中，需要注意的：
 *      1）当有写锁时，除了获得写锁的这个线程可以获得读锁外，其他线程不能获得读锁；
 *      2）当有读锁时，写锁就不能获得；
 * PS：底层源码实现，还是有待加强！！
 */
class MyCache{

    private volatile Map<String,Object> map = new HashMap<>();
    // 创建读写锁
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void put(String key,Object value){
        // 加上写锁
        readWriteLock.writeLock().lock();
        try{

            System.out.println(Thread.currentThread().getName() + "\t" + "写入数据开始...\t" + key);
            try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
            map.put(key,value);
            System.out.println(Thread.currentThread().getName() + "\t" + "写入数据完成！！");

        } catch(Exception e){
            e.printStackTrace();
        } finally{
            readWriteLock.writeLock().unlock();
        }
    }

    public void get(String key){
        // 加上读锁
        readWriteLock.readLock().lock();
        try{

            System.out.println(Thread.currentThread().getName() + "\t" + "读取数据开始...");
            try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t" + "读取数据结束！！\t" + o);

        } catch(Exception e){
            e.printStackTrace();
        } finally{
            readWriteLock.readLock().unlock();
        }
    }

}
